/*Scala code license:

 Copyright 2009/10 Osman Pub

 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 this file except in compliance with the License.

 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for the
 specific language governing permissions and limitations under the License.

 */
/**
   Orginal java code credits:
 * @author Pepijn Van Eeckhoudt
 */
package common

import com.sun.opengl.util.FPSAnimator
import java.awt._
import java.awt.event._
import javax.swing._
import javax.media.opengl._
import scala.collection.mutable._

object GLDisplay {
  def apply(title: String, fullScreen: Boolean = false): GLDisplay =
    new GLDisplay(title, DEFAULT_WIDTH, DEFAULT_HEIGHT, fullScreen,
                  new GLCapabilities)
  def apply(title: String, fullScreen: Boolean, caps: GLCapabilities) =
    new GLDisplay(title, DEFAULT_WIDTH, DEFAULT_HEIGHT, fullScreen, caps)

  private val DEFAULT_WIDTH = 800
  private val DEFAULT_HEIGHT = 600
}

class GLDisplay(title: String, width: Int, height: Int, fullScreen: Boolean,
                caps: GLCapabilities) {

  private val device =
    GraphicsEnvironment.getLocalGraphicsEnvironment getDefaultScreenDevice

  private val displayModes = bestDisplayModes

  private val glCanvas = new GLCanvas(caps)
  glCanvas setSize(width, height); glCanvas setIgnoreRepaint true

  private val frame = new JFrame(title)
  frame.getContentPane setLayout new BorderLayout
  frame.getContentPane add(glCanvas, BorderLayout CENTER)

  private val animator = new FPSAnimator(glCanvas, 60)
  animator setRunAsFastAsPossible false

  def start {
    try {
      if(fullScreen) setFullScreen(frame, displayModes head)
      else {
        val screenSize = Toolkit.getDefaultToolkit getScreenSize;
        frame setUndecorated fullScreen
        frame addWindowListener new WindowAdapter {
          override def windowClosing(e: WindowEvent) =  stop
        }

        frame setSize(frame.getContentPane getPreferredSize)
        frame setLocation((screenSize.width - frame.getWidth) / 2,
          (screenSize.height - frame.getHeight) / 2)
        frame setVisible true
      }

      glCanvas requestFocus; animator start
    } catch { case e: Exception => e printStackTrace }
  }

  def stop =
    try {
      animator stop;
      if(fullScreen) device setFullScreenWindow(null)
      frame dispose
    } catch { case e: Exception => e printStackTrace }
    finally { System exit 0 }

  def addGLEventListener(glEventListener: GLEventListener) =
    glCanvas addGLEventListener glEventListener
  def addKeyListener(l: KeyListener) = glCanvas addKeyListener l
  def addMouseListener(l: MouseListener) = glCanvas addMouseListener l
  def addMouseMotionListener(l: MouseMotionListener) =
    glCanvas addMouseMotionListener l

  def removeGLEventListener(glEventListener: GLEventListener) =
    glCanvas removeGLEventListener glEventListener
  def removeKeyListener(l: KeyListener) = glCanvas removeKeyListener l
  def removeMouseListener(l: MouseListener) = glCanvas removeMouseListener l
  def removeMouseMotionListener(l: MouseMotionListener) =
    glCanvas removeMouseMotionListener l

  def fsHeight = displayModes.head getHeight
  def fsWidth = displayModes.head getWidth
  def fullScreenWindow = device getFullScreenWindow match { case o: JFrame => o }

  def getTitle = frame getTitle
  def setTitle(title: String) = frame setTitle title

  private def bestDisplayModes: scala.List[DisplayMode] = {
    // min display is 1024x768
    val widths = (device.getDisplayModes filter { _.getWidth >= 1024 }
                  toList).distinct map { _.getWidth }
    val heights = widths map { w => (device.getDisplayModes find
                                    (_.getWidth == w)).get.getHeight }
    // we reverse the list so the highest refresh rates can be found first
    val modes = device.getDisplayModes.toList reverse;
    (widths zip heights) map
      { d => modes.find (m => (m.getWidth == d._1) &&
                              (m.getHeight == d._2)).get }
  }

  private def setFullScreen(frame: JFrame, displayMode: DisplayMode) {
    frame setDefaultCloseOperation JFrame.EXIT_ON_CLOSE
    frame setUndecorated true; frame setIgnoreRepaint true
    frame setResizable false; device setFullScreenWindow frame

    if(device.isDisplayChangeSupported && displayMode != null)	{
      try { device setDisplayMode displayMode }
      catch { case e: IllegalArgumentException => }
      // fix for mac os x
      frame setSize(displayMode getWidth, displayMode getHeight)
    }
    // avoid potential deadlock in 1.4.1_02
    try { EventQueue invokeAndWait(
        new Runnable { override def run { frame createBufferStrategy 2 }})
    } catch { case e: Exception  => }
  }
}
